#include "StdAfx.h"
#include <stdio.h>
#include "GameEntry.h"
#include "colors.h"
#include "LOG.h"
#include "util.h"
#include <ctime>
#include <sstream>
#include <string>

const float speed = 0.5f;

// HELPER FUNCTIONS
// string tokenizing
std::vector<std::string> &split(const std::string &s, char delim, std::vector<std::string> &elems)
{
    std::stringstream ss(s);
    std::string item;
    while(std::getline(ss, item, delim)) elems.push_back(item);
    return elems;
}
std::vector<std::string> split(const std::string &s, char delim)
{
    std::vector<std::string> elems;
    return split(s, delim, elems);
}

// string conversions
std::string convertToString(int i)
{
	std::string s;
	std::stringstream out;
	out << i;
	return out.str();
}

std::string convertToString(float f)
{
	std::string s;
	std::stringstream out;
	out << f;
	return out.str();
}

// intersection of a circle @ (x3,z3) with line_seg[(x1,z1)->(x2,z2)] with radius r
bool circle_intersect_lineseg(float x1, float z1, float x2, float z2, float x3, float z3, float r)
{
	float u = ((x3 - x1)*(x2 - x1) + (z3 - z1)*(z2 - z1)) / ((x2 - x1)*(x2 - x1) + (z2 - z1)*(z2 - z1));
	if (u < 0 || u > 1) return false;

    float a = (x2-x1)*(x2-x1) + (z2-z1)*(z2-z1);
    float b = 2*( (x2-x1)*(x1-x3) + (z2-z1)*(z1-z3) );
    float c = x3*x3 + z3*z3 + x1*x1 + z1*z1 - 2*(x3*x1 + z3*z1) - r*r;
            
    float deter = b*b - 4*a*c;
    if (deter < 0) return false;
    if (deter >= 0) return true;

	return false;
}

// CONSTRUCTOR
GameEntry::GameEntry(HINSTANCE hInstance, char* gameName) : DemoApp(hInstance, gameName),
	pMesh(NULL),
	numberOfBlocks(0),
	bConnected(false),
	PLAYER_ID(-1),
	PING_SERVER(NULL)
{
	lastTime = time(NULL); // time since Jan 1, 1970
	otherPlayers = std::map<int, Player*>();
}

// DESTRUCTOR
GameEntry::~GameEntry(void)
{
	SendExitMessage();

	if (pMesh != NULL) delete pMesh;

	// since we're using pointers, we need to manually delete each item within the map
	if (otherPlayers.size() > 0)
	{
		for(std::map<int, Player*>::iterator it = otherPlayers.begin(); it != otherPlayers.end(); ++it) 
			delete it->second;
	}
	if (chasers.size() > 0)
	{
		for(std::map<int, Chaser*>::iterator it = chasers.begin(); it != chasers.end(); ++it) 
			delete it->second;
	}
}

void GameEntry::SendExitMessage()
{
	if (bConnected)
	{
		// send an exit message to the server
		char* exitMsg = new char[128];
		sprintf(exitMsg, "EXIT:%d", PLAYER_ID);
		hNetwork.send(exitMsg);
		delete[] exitMsg;
	}
}

/******************************************************************
Purpose: process the messages recieved from the server
Descripton:
Return:
******************************************************************/
void GameEntry::parseMessage(std::string msg)
{
	// extract the message header
	int header_loc = msg.find_first_of(":");
	std::string header = msg.substr(0,header_loc);
	std::string body = msg.substr(header_loc+1, msg.length());
	std::vector<std::string> tokens = split(body,';'); // split up the message into it's components, ignore the last one

	// initization of the game world
	if (header.compare("INIT_WORLD") == 0)
	{
		float player_init_x = 0.0f;
		float player_init_z = 0.0f;
		int player_type;
		for (std::vector<std::string>::iterator it = tokens.begin(); it!=tokens.end(); ++it)
		{
			std::vector<std::string> parameters = split(*it, ':');
			for (std::vector<std::string>::iterator p = parameters.begin(); p!=parameters.end(); ++p)
			{
				std::vector<std::string> param = split(*p, '=');

				if (param[0].compare("ID") == 0) PLAYER_ID = atoi(param[1].c_str());
				if (param[0].compare("P_T") == 0) player_type = atoi(param[1].c_str());
				if (param[0].compare("P_X") == 0) player_init_x = atoi(param[1].c_str());
				if (param[0].compare("P_Z") == 0) player_init_z = atoi(param[1].c_str());
				if (param[0].compare("C_X") == 0) CELLSIZE_X = atof(param[1].c_str());
				if (param[0].compare("C_Z") == 0) CELLSIZE_Z = atof(param[1].c_str());
				if (param[0].compare("G_X") == 0) GRIDSIZE_X = atoi(param[1].c_str());
				if (param[0].compare("G_Z") == 0) GRIDSIZE_Z = atoi(param[1].c_str());
			}

			 // ensure that we have all parameters before creating the world
			if (parameters.size() == 8)
			{
				isPlayerBot = player_type == 2;

				initGame();

				// create the manual players/chasers
				if (isPlayerBot == false)
				{
					player.m_fMovSpd = configReader.m_PlayerMovSpd;
					player.m_fTurnSpd = configReader.m_PlayerTurnSpd;
					player.block->m_Pos.x = player_init_x;
					player.block->m_Pos.z = player_init_z;
					player.camera.setPosition(D3DXVECTOR3(player.block->m_Pos.x, player.camera.m_pos.y, player.block->m_Pos.z));
					isPlayerBot = false;

					drArea.set(player.block->m_Pos, player.m_fAngle);
				}
				else
				{
					chaser.m_fMovSpd = configReader.m_PlayerMovSpd;
					chaser.m_fTurnSpd = configReader.m_PlayerTurnSpd;
					chaser.block->m_Pos.x = player_init_x;
					chaser.block->m_Pos.z = player_init_z;
					chaser.camera.setPosition(D3DXVECTOR3(chaser.block->m_Pos.x, chaser.camera.m_pos.y + Camera::DEFAULT_TOP_DOWN_OFFSET, chaser.block->m_Pos.z));

					drArea.set(chaser.block->m_Pos, chaser.m_fAngle);
				}
				
				// maunal chaser, just load the chaser model
				if (player_type == 3)
				{
					player.Load("data/Ferrari.x");
				}

				if (player_type == 2)
				{
					chaser.Load("data/Ferrari.x");
				}

				// create the ping message by adding our player_id to 'P'
				PING_SERVER = new char[16];
				sprintf(PING_SERVER, "P:%d", PLAYER_ID);

				hNetwork.send("WORLD_INFO"); // now we ask for the obstacles
			}
		}
	}

	// information about where all obstacles are located
	if (header.compare("OBSTACLES") == 0)
	{
		for (std::vector<std::string>::iterator it = tokens.begin(); it!=tokens.end(); ++it)
		{
			std::vector<std::string> parameters = split(*it, ':');
			int x = 0;
			int z = 0;
			BlockType t = BLOCK_REG;
			for (std::vector<std::string>::iterator p = parameters.begin(); p!=parameters.end(); ++p)
			{

				std::vector<std::string> param = split(*p, '=');
				if (param[0].compare("type") == 0)
				{
					if (param[1].compare("G") == 0) t = BLOCK_GOAL;
					if (param[1].compare("R") == 0) t = BLOCK_REG;
					if (param[1].compare("P") == 0) t = BLOCK_PLAYER;
					if (param[1].compare("C") == 0) t = BLOCK_CHASER;
				}
				if (param[0].compare("X") == 0) x = atoi(param[1].c_str());
				if (param[0].compare("Z") == 0) z = atof(param[1].c_str());
			}
			obstacleMap.AddObstacle(t, x, z);
		}
		
		// now that we have all the obstacles, lets tell the server we are in the game
		char* updateMsg = new char[256];
		if (isPlayerBot == false) sprintf(updateMsg, "UP:ID=%d:X=%.1f:Z=%.1f:A=%.1f;", PLAYER_ID, player.block->m_Pos.x, player.block->m_Pos.z, player.m_fAngle);
		else sprintf(updateMsg, "UP:ID=%d:X=%.1f:Z=%.1f:A=%.1f;", PLAYER_ID, chaser.block->m_Pos.x, chaser.block->m_Pos.z, chaser.m_fAngle);
		hNetwork.send(updateMsg);

		delete[] updateMsg;
	}

	// updated player locations and so on
	if (header.compare("UP") == 0)
	{
		for (std::vector<std::string>::iterator it = tokens.begin(); it!=tokens.end(); ++it)
		{
			std::vector<std::string> parameters = split(*it, ':');
			int player_id = -1; // start with invalid id
			float x = -1.0f;
			float z = -1.0f;
			float angle = 0.0f;
			int type = -1;

			for (std::vector<std::string>::iterator p = parameters.begin(); p!=parameters.end(); ++p)
			{
				std::vector<std::string> param = split(*p, '=');
				if (param[0].compare("ID") == 0) player_id = atoi(param[1].c_str());
				if (param[0].compare("T") == 0) type = atoi(param[1].c_str());
				if (param[0].compare("X") == 0) x = atof(param[1].c_str());
				if (param[0].compare("Z") == 0) z = atof(param[1].c_str());
				if (param[0].compare("A") == 0) angle = atof(param[1].c_str());
			}

			// ignore any ids which are us
			if (player_id != PLAYER_ID)
			{
				D3DXVECTOR3 playerPos = D3DXVECTOR3(x, 0.0f, z);

				if (type == 2)
				{
					chaser.block = new Block(playerPos, D3DXVECTOR3(CELLSIZE_X, CELLSIZE_Y, CELLSIZE_Z));
					chaser.block->setType(BLOCK_CHASER);
				}
				else
				{
					// now that we have all the needed info, update the player
					if (otherPlayers.find(player_id) == otherPlayers.end()) // hash_map doesn't have the player, must be new so add it to our map
					{
						Player *newPlayer = new Player();
						
						newPlayer->camera.setPerspective(60.0f, (float)(this->mWndWidth)/this->mWndHeight, 1.0f, 5000.0f);
						newPlayer->camera.setPosition(playerPos + D3DXVECTOR3(0.0f, Camera::DEFAULT_FIRST_PERSON_OFFSET, 0.0f));
						newPlayer->camera.setAcceleration(D3DXVECTOR3(1.0f, 1.0f, 1.0f));
						newPlayer->camera.setVelocity(D3DXVECTOR3(speed, speed, speed));
						newPlayer->camera.setCurrentVelocity(D3DXVECTOR3(speed, speed, speed));
						newPlayer->camera.switchMode(); // start as FPS, change to topdown so other players are actually rendered!!!

						newPlayer->block = new Block(newPlayer->camera.m_pos, D3DXVECTOR3(CELLSIZE_X, CELLSIZE_Y, CELLSIZE_Z));

						if (type == 3)
						{
							newPlayer->block->setType(BLOCK_CHASER);
							newPlayer->Load("data/Ferrari.x");
						}
						else
						{
							newPlayer->block->setType(BLOCK_PLAYER);
						}

						newPlayer->m_fAngle = angle;
						otherPlayers[player_id] = newPlayer;
					}
					else // otherwise update the player with the new information
					{
						otherPlayers[player_id]->block->m_Pos.x = x;
						otherPlayers[player_id]->block->m_Pos.y = Camera::DEFAULT_FIRST_PERSON_OFFSET;
						otherPlayers[player_id]->block->m_Pos.z = z;
						otherPlayers[player_id]->m_fAngle = angle;
					}
				}

			}
		}
	}

	// remove the player from our lists as it has exited
	if (header.compare("EXIT") == 0)
	{
		int player_id = atoi(body.c_str());
		std::map<int, Player*>::iterator exitedPlayer = otherPlayers.find(player_id);
		if (exitedPlayer != otherPlayers.end()) otherPlayers.erase(exitedPlayer);
	}

	// received a DR update message
	if (header.compare("DR") == 0)
	{
		for (std::vector<std::string>::iterator it = tokens.begin(); it!=tokens.end(); ++it)
		{
			std::vector<std::string> parameters = split(*it, ':');
			int player_id = -1; // start with invalid id
			float x = -1.0f;
			float z = -1.0f;
			float angle = 0.0f;

			for (std::vector<std::string>::iterator p = parameters.begin(); p!=parameters.end(); ++p)
			{
				std::vector<std::string> param = split(*p, '=');
				if (param[0].compare("ID") == 0) player_id = atoi(param[1].c_str());
				if (param[0].compare("X") == 0) x = atof(param[1].c_str());
				if (param[0].compare("Z") == 0) z = atof(param[1].c_str());
				if (param[0].compare("A") == 0) angle = atof(param[1].c_str());
			}

			// ignore any ids which are us
			if (player_id != PLAYER_ID)
			{
				// now that we have all the needed info, update the player
				if (otherPlayers.find(player_id) != otherPlayers.end()) // hash_map doesn't have the player, must be new so add it to our map
				{
					otherPlayers[player_id]->block->m_Pos.x = x;
					otherPlayers[player_id]->block->m_Pos.y = Camera::DEFAULT_FIRST_PERSON_OFFSET;
					otherPlayers[player_id]->block->m_Pos.z = z;
					otherPlayers[player_id]->m_fAngle = angle;
				}
			}
		}
	}
}

int GameEntry::updateGameState(long time)
{
	int rc = 0;

	// check server messages
	if (!hNetwork.messages.empty())
	{
		parseMessage(hNetwork.messages.front());
		hNetwork.messages.pop();
	}

	// only start doing things (such as rendering) when we have connected to the server
	// since we need the server to tell us what to render (players, board size etc..)
	if (bConnected)
	{		
		
		mInput->poll(); // poll the input
			
		if (mInput->keyDown(DIK_ESCAPE)) rc = 1; // check if escape key was pressed
		if (mInput->keyDown(DIK_TAB)) player.camera.switchMode(); // change from FPS to topdown and vice versa

		update(time);
	}

	return(rc);
}

void GameEntry::processKeys(void)
{
	if (mInput->keyDown(DIK_B))
		pMesh->bShowWireframe = !pMesh->bShowWireframe; 
}

int GameEntry::renderFrame(int time)
{
	int i = 0;
	int rc = D3D_OK;
	if (bConnected)
	{
		if (isPlayerBot)
		{
			rc = md3dDev->SetTransform(D3DTS_VIEW, &chaser.camera.getViewMatrix()); // inform direct3d about the view transformation
			rc = md3dDev->SetTransform(D3DTS_PROJECTION, &chaser.camera.getProjectionMatrix()); // inform direc3d about the proejctions tranfsormation
		}
		else
		{
			rc = md3dDev->SetTransform(D3DTS_VIEW, &player.camera.getViewMatrix()); // inform direct3d about the view transformation
			rc = md3dDev->SetTransform(D3DTS_PROJECTION, &player.camera.getProjectionMatrix()); // inform direc3d about the proejctions tranfsormation
		}

		// clear the window
		rc = md3dDev->Clear(0, NULL, D3DCLEAR_TARGET, D3DCOLOR_XRGB(0, 0, 0), 1.0f, 0);
		rc = md3dDev->Clear(0, NULL, D3DCLEAR_ZBUFFER, D3DCOLOR_XRGB(0, 0, 0), 1.0f, 0);

		if (rc != D3D_OK) { i++; }

		md3dDev->BeginScene();
		md3dDev->SetFVF(DEFAULT_VERTEX_FVF);

		pMesh->render(time);
		obstacleMap.render(time);

		// render the DR triangle area
		drArea.Render();

		if (isPlayerBot) chaser.Render();
		else player.Render();
		

		for (std::map<int, Player*>::iterator iter = otherPlayers.begin(); iter != otherPlayers.end(); iter++)
		{
			iter->second->Render();
        }

		md3dDev->EndScene();

		rc = md3dDev->Present(NULL, NULL, NULL, NULL);   // displays the created frame on the screen
	}
	if (rc != D3D_OK) { i++; }
	return i;
}

int GameEntry::initGame(void)
{
	bConnected = true;
	initCamera();
	initMapObjects();
	initLights();

	//md3dDev->SetRenderState(D3DRS_ALPHABLENDENABLE, TRUE);
	//md3dDev->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_SRCALPHA);
	//md3dDev->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA);
	if (isPlayerBot)
	{
		chaser.camera.setPosition(D3DXVECTOR3(chaser.camera.m_pos.x, Camera::DEFAULT_FIRST_PERSON_OFFSET, chaser.camera.m_pos.z));
		chaser.searchRadius = (GRIDSIZE_Z/4) *CELLSIZE_Z;
	}
	else
	{
		player.camera.setPosition(D3DXVECTOR3(player.camera.m_pos.x, Camera::DEFAULT_FIRST_PERSON_OFFSET, player.camera.m_pos.z));
	}

	// set the DR update triangle window to whatever is read from the config
	drArea.m_fBase = configReader.m_fDR_BASE;
	drArea.m_fHeight = configReader.m_fDR_HEIGHT;

	return 0;
}

/******************************************************************
Purpose: initializes camera and perspective for rendering
Descripton:
Return:
******************************************************************/
void GameEntry::initCamera(void)
{
	if (isPlayerBot == false)
	{
		player.camera.setPerspective(60.0f, (float)(this->mWndWidth)/this->mWndHeight, 1.0f, 5000.0f);
		player.block = new Block(player.camera.m_pos, D3DXVECTOR3(CELLSIZE_X, CELLSIZE_Y, CELLSIZE_Z));
		player.block->setType(BLOCK_PLAYER);
	}
	else
	{
		chaser.camera.setPerspective(60.0f, (float)(this->mWndWidth)/this->mWndHeight, 1.0f, 5000.0f);
		chaser.block = new Block(chaser.camera.m_pos, D3DXVECTOR3(CELLSIZE_X, CELLSIZE_Y, CELLSIZE_Z));
		chaser.block->setType(BLOCK_CHASER);
	}
}

/******************************************************************
Purpose: initializes networking with a server
Descripton:
Return:
******************************************************************/
void GameEntry::initNetworking(void)
{
	hNetwork.serverIPAdd = configReader.m_ServerIP;
	hNetwork.serverPort = configReader.m_ServerPort;
	
	// tell the server that we're a new connection and send it the player type (from config reader)
	char* newMsg = new char[16];
	sprintf(newMsg, "NEW:%d\n",configReader.m_PlayerType);
	hNetwork.send(newMsg);
	delete[] newMsg;

	hNetwork.lastUpdate = GetTickCount();
}

/******************************************************************
Purpose: creates a bunch of blocks to be placed on the plane
Descripton:
Return:
******************************************************************/
void GameEntry::initMapObjects()
{
	pMesh = new SimpleMesh(GRIDSIZE_X, GRIDSIZE_Z, CELLSIZE_X, CELLSIZE_Z);
	//pMesh->setScale(2.0, 4.0, 2.0);
	if (pMesh == NULL) return;

	char* buf = new char[512];
	sprintf(buf, "Terrain @ (%.4g, %.4g, %.4g) with dimensions[%.4g x %.4g]\n",
		pMesh->m_Pos.x, pMesh->m_Pos.y, pMesh->m_Pos.z, pMesh->nRows*pMesh->cellsize_x, pMesh->nCols*pMesh->cellsize_z);
	OutputDebugString(buf);
	delete[] buf;

	obstacleMap.init(0,0, pMesh->nRows*CELLSIZE_X, pMesh->nCols*CELLSIZE_Z);
}


/******************************************************************
Purpose: initializes the lighting.
Descripton:
Return:
******************************************************************/
void GameEntry::initLights()
{
	// directional light properties
	D3DXVECTOR3 direction;
	D3DLIGHT9 light;
	D3DMATERIAL9 material;

	ZeroMemory(&light, sizeof(light));
	light.Type      = D3DLIGHT_DIRECTIONAL;
	light.Ambient	= WHITE;
	light.Diffuse   = WHITE;
	light.Specular	= WHITE;
	light.Range		= 1000.0f;

	// Create a direction for our light - it must be normalized  
	direction = D3DXVECTOR3(0.0f, -3.0f, 0.0f);
	D3DXVec3Normalize((D3DXVECTOR3*)&light.Direction, &direction);
    
	md3dDev->SetLight(0, &light);
	md3dDev->LightEnable(0, TRUE);

	ZeroMemory(&material, sizeof(D3DMATERIAL9));    // clear out the struct for use
	material.Diffuse = WHITE;    // set diffuse color to white
	material.Ambient = WHITE;    // set ambient color to white
	md3dDev->SetMaterial(&material);    // set the globably-used material to &material

	md3dDev->SetRenderState(D3DRS_LIGHTING, TRUE);
	md3dDev->SetRenderState(D3DRS_CULLMODE,D3DCULL_NONE);
	md3dDev->SetRenderState(D3DRS_ZENABLE, D3DZB_TRUE); // turn on the z-buffer
	md3dDev->SetRenderState(D3DRS_AMBIENT, WHITE);
	md3dDev->SetRenderState(D3DRS_FILLMODE, D3DFILL_SOLID);
}

/******************************************************************
Purpose: Get the direction that the camera is pointing at.
Descripton:
Return:
******************************************************************/
void GameEntry::movePlayer()
{
	static bool bForward		= false;
    static bool bBackward		= false;
    static bool bTurnRight		= false;
    static bool bTurnLeft		= false;

	bool bShift = mInput->keyDown(DIK_LSHIFT) || mInput->keyDown(DIK_RSHIFT);

	// move foward
	if (mInput->keyDown(DIK_W))
	{
		if (!bForward)
		{
			bForward = true;
		}
		player.moveForward(bShift);
	}
	else
	{
		bForward = false;
	}

	// move backwards
	if (mInput->keyDown(DIK_S))
	{
		if (!bBackward)
		{
			bBackward = true;
		}
		player.moveBackward(bShift);
	}
	else
	{
		bBackward = false;
	}

	// turn left
	if (mInput->keyDown(DIK_A))
	{
		if (!bTurnLeft)
		{
			bTurnLeft = true;
		}
		player.turnLeft();
	}
	else
	{
		bTurnLeft = false;
	}

	// turn right
	if (mInput->keyDown(DIK_D))
	{
		if (!bTurnRight)
		{
			bTurnRight = true;
		}
		player.turnRight();
	}
	else
	{
		bTurnRight = false;
	}

	// handle the mouse movements to turn the camera in FPS
	if (player.camera.isFirstPerson())
	{
		float dx = (float)this->mInput->mouseDX();
		float dy = (float)this->mInput->mouseDY();
		float dz = (float)this->mInput->mouseDZ();
		player.camera.rotate(-dx, -dy, 0.0f);
	}
}

void GameEntry::update(long time)
{
	// constantly ping the server, if we recieve anything other than ACK, we have to handle it
	// used for DR updating as the server can't
	if (bConnected)
	{
		hNetwork.send(PING_SERVER);

		DWORD currentTime = GetTickCount();
		if ((hNetwork.lastUpdate + configReader.m_MsgDelay) <= currentTime) // find a way to get it to within X-ms
		{
			char* updateMsg = new char[256];
			if (isPlayerBot == false) sprintf(updateMsg, "UP:ID=%d:X=%.1f:Z=%.1f:A=%.1f;", PLAYER_ID, player.block->m_Pos.x, player.block->m_Pos.z, player.m_fAngle);
			else sprintf(updateMsg, "UP:ID=%d:X=%.1f:Z=%.1f:A=%.1f;", PLAYER_ID, chaser.block->m_Pos.x, chaser.block->m_Pos.z, chaser.m_fAngle);
			hNetwork.send(updateMsg);

			delete[] updateMsg;
			hNetwork.lastUpdate = currentTime;
		}
	}


	if (isPlayerBot)
	{
		// find a player to target
		if (otherPlayers.size() > 0)
		{
			float closest_dist = 999999.9f; // huge distance, we want to find the closest
			int index = 0;
			for(std::map<int, Player*>::iterator it = otherPlayers.begin(); it != otherPlayers.end(); ++it) 
			{
				D3DXVECTOR3 dir = (it->second->block->m_Pos - chaser.block->m_Pos);
				float dist = dir.x*dir.x + dir.z*dir.z;
				if (dist < closest_dist)
				{
					closest_dist = dist;
					index = it->first;
				}
			}	
			chaser.update(otherPlayers[index]->block->m_Pos, time);
		}
		
		else
		{
			chaser.update(D3DXVECTOR3(pMesh->cellsize_x * pMesh->nRows *2.0f, 0.0f, pMesh->cellsize_z * pMesh->nCols *2.0f), time);
		}

		Block* chaser_blocked = obstacleMap.collides(chaser.block->BottomCenterPos());
		if(chaser_blocked!=NULL && chaser_blocked->type != BLOCK_GOAL) // chasers ignore the goal
		{
			chaser.block->m_Pos = chaser.block->Last_m_Pos;
		}
	}
	else
	{
		movePlayer();
		Block* player_blocked = obstacleMap.collides(player.block->BottomCenterPos());
		if(player_blocked!=NULL)
		{
			player.block->m_Pos = player.block->Last_m_Pos;
			player.camera.setPosition(D3DXVECTOR3(player.block->m_Pos.x,
				player.block->m_Pos.y + Camera::DEFAULT_TOP_DOWN_OFFSET,
				player.block->m_Pos.z));
		}

		// found the goal, send exit message to server and then exit the game
		if (player_blocked != NULL)
		{
			if (player_blocked->type == BLOCK_GOAL)
			{
				MessageBox(0, "Touched the Goal!", 0, 0);
				SendExitMessage();
				exit(0);
			}
		}
	}

	if (isPlayerBot == false)
	{
		// check if other players collide with us, if they are a chaser its game over!
		for(std::map<int, Player*>::iterator it = otherPlayers.begin(); it != otherPlayers.end(); ++it) 
		{
			if (it->second->block->type == BLOCK_CHASER)
			{
				D3DXVECTOR3 bottomPos = it->second->block->BottomCenterPos();
				D3DXVECTOR3 dir = (it->second->block->m_Pos - player.block->m_Pos);
				float dist = dir.x*dir.x + dir.z*dir.z;
				if (dist < configReader.m_fCollisionRadius)
				{
					MessageBox(0, "Chaser has caught you!", 0, 0);
					SendExitMessage();
					exit(0);
				}
			}
		}
	}



	// update all other players with their DR positions
	for(std::map<int, Player*>::iterator it = otherPlayers.begin(); it != otherPlayers.end(); ++it) 
	{
		//drPredictions[it->first] += it->second
		it->second->moveForward(false); // assume all other players just move forward
	}

	if (isPlayerBot == false)
	{
		
		// our prediction area DOES NOT contains the player, send a DR update
		if (!drArea.contains(player.block->m_Pos))
		{
			drArea.set(player.block->m_Pos, player.m_fAngle);
			char* drMsg = new char[256];
			sprintf(drMsg, "DR:ID=%d:X=%.1f:Z=%.1f:A=%.1f;", PLAYER_ID, player.block->m_Pos.x, player.block->m_Pos.z, player.m_fAngle);
			hNetwork.send(drMsg);
			delete[] drMsg;
		}
	}
	else
	{
		if (!drArea.contains(chaser.block->m_Pos))
		{
			drArea.set(chaser.block->m_Pos, chaser.m_fAngle);
			char* drMsg = new char[256];
			sprintf(drMsg, "DR:ID=%d:X=%.1f:Z=%.1f:A=%.1f;", PLAYER_ID, player.block->m_Pos.x, player.block->m_Pos.z, player.m_fAngle);
			hNetwork.send(drMsg);
			delete[] drMsg;
		}
	}

	
	updateBounds(time);
}

void GameEntry::updateBounds(long time)
{
	if (isPlayerBot)
	{
		chaser.camera.updateViewMatrix(); // update bot view
		chaser.block->m_Pos = ensureBounds(chaser.block->m_Pos);
		chaser.camera.setPosition(ensureBounds(chaser.camera.getPosition()));
	}
	else
	{
		player.camera.updateViewMatrix(); // update player view

		// update player position
		player.block->m_Pos = ensureBounds(player.block->m_Pos);
		player.camera.setPosition(ensureBounds(player.camera.getPosition()));
	}
	
	// update the other player positions using DR predictions
}

// prevent the anything from moving too far outside of the scene.
const D3DXVECTOR3 GameEntry::ensureBounds(const D3DXVECTOR3 &pos)
{
    D3DXVECTOR3 newPos(pos);
	D3DXVECTOR3 CAMERA_BOUNDS_MAX(pMesh->nRows*pMesh->cellsize_x - 8.0f, Camera::DEFAULT_FIRST_PERSON_OFFSET*200.0f, pMesh->nRows*pMesh->cellsize_z - 8.0f);
	D3DXVECTOR3 CAMERA_BOUNDS_MIN(0.0f, 0.0f, 0.0f);

    if (pos.x > CAMERA_BOUNDS_MAX.x) newPos.x = CAMERA_BOUNDS_MAX.x;
    if (pos.x < CAMERA_BOUNDS_MIN.x) newPos.x = CAMERA_BOUNDS_MIN.x;
    if (pos.y > CAMERA_BOUNDS_MAX.y) newPos.y = CAMERA_BOUNDS_MAX.y;
    if (pos.y < CAMERA_BOUNDS_MIN.y) newPos.y = CAMERA_BOUNDS_MIN.y;
    if (pos.z > CAMERA_BOUNDS_MAX.z) newPos.z = CAMERA_BOUNDS_MAX.z;
    if (pos.z < CAMERA_BOUNDS_MIN.z) newPos.z = CAMERA_BOUNDS_MIN.z;
	return newPos;
}
